<script setup>
import { ref, onMounted, onUpdated, onUnmounted, onBeforeMount, onBeforeUpdate, onBeforeUnmount } from 'vue';

// 用于存储生命周期日志
const lifecycleLogs = ref([]);

// 用于触发更新的计数器
const counter = ref(0);

// 用于控制子组件的显示
const showChild = ref(true);

// 记录日志的辅助函数
function log(event, color = '#333') {
  const timestamp = new Date().toLocaleTimeString();
  lifecycleLogs.value.push({
    event,
    timestamp,
    color
  });
  // 限制日志数量，保持界面整洁
  if (lifecycleLogs.value.length > 15) {
    lifecycleLogs.value.shift();
  }
}

// 模拟外部API调用和数据
const mockUserData = { name: '示例用户', age: 25 };
const mockApi = {
  fetchUserData: () => new Promise(resolve => setTimeout(() => resolve(mockUserData), 500)),
  saveUserData: (data) => new Promise(resolve => setTimeout(() => resolve({ success: true }), 300))
};

// 用户数据状态
const userData = ref(null);
const isLoading = ref(false);
const serverMessage = ref('');

// 生命周期钩子 - 具体业务场景实现
onBeforeMount(() => {
  log('onBeforeMount', '#4caf50');
  // 业务场景1: 在组件挂载前准备初始数据，设置默认值
  // 这里可以初始化一些不需要DOM的逻辑
  serverMessage.value = '准备加载用户数据...';
});

onMounted(() => {
  log('onMounted', '#2196f3');
  // 业务场景2: 组件挂载后进行DOM操作、API调用、事件监听设置
  isLoading.value = true;
  // 模拟API调用获取用户数据
  mockApi.fetchUserData().then(data => {
    userData.value = data;
    serverMessage.value = '用户数据加载成功';
  }).catch(error => {
    serverMessage.value = '数据加载失败';
    console.error('获取用户数据失败:', error);
  }).finally(() => {
    isLoading.value = false;
  });
  
  // 添加全局事件监听（在真实应用中使用）
  // window.addEventListener('resize', handleResize);
});

// 使用一个单独的标志来避免递归更新
let updateInProgress = false;
onBeforeUpdate(() => {
  if (updateInProgress) return;
  updateInProgress = true;
  
  // 业务场景3: 在数据更新前进行验证、计算或数据准备
  // 例如：在用户输入更新前进行表单验证
  log('onBeforeUpdate', '#ff9800');
  serverMessage.value = '组件即将更新...';
  
  // 重置标志，避免影响后续更新
  setTimeout(() => {
    updateInProgress = false;
  }, 0);
});

onUpdated(() => {
  if (updateInProgress) return;
  updateInProgress = true;
  
  // 业务场景4: 更新后执行DOM操作、触发动画、滚动同步等
  log('onUpdated', '#9c27b0');
  serverMessage.value = '组件已更新';
  
  // 模拟更新后的数据保存
  if (userData.value && counter.value % 3 === 0 && counter.value > 0) {
    serverMessage.value = '正在保存数据...';
    mockApi.saveUserData(userData.value).then(() => {
      serverMessage.value = '数据已保存';
    });
  }
  
  // 重置标志
  setTimeout(() => {
    updateInProgress = false;
  }, 0);
});

onBeforeUnmount(() => {
  log('onBeforeUnmount', '#f44336');
  // 业务场景5: 组件卸载前清理资源、保存状态、取消订阅
  serverMessage.value = '组件即将卸载...';
  // 清理定时器、事件监听器等
  // window.removeEventListener('resize', handleResize);
  
  // 模拟在卸载前保存用户会话状态
  if (userData.value) {
    console.log('保存用户会话状态:', userData.value);
  }
});

onUnmounted(() => {
  log('onUnmounted', '#795548');
  // 业务场景6: 组件完全卸载后进行最终清理
  console.log('组件已完全卸载，释放所有资源');
  // 这里的日志只会在控制台显示，因为组件已卸载
});

// 方法
const incrementCounter = () => {
  counter.value++;
  // 同时更新用户数据以触发组件更新生命周期
  if (userData.value) {
    userData.value = { ...userData.value, lastUpdated: new Date().toLocaleTimeString() };
  }
};

const toggleChild = () => {
  showChild.value = !showChild.value;
};

const clearLogs = () => {
  lifecycleLogs.value = [];
};

const updateUserData = () => {
  if (userData.value) {
    userData.value = {
      ...userData.value,
      name: `用户${counter.value}`,
      age: 25 + counter.value % 10
    };
  }
};

// 子组件定义
const LifecycleChild = {
  setup() {
    // 使用非响应式方式记录日志，避免触发递归更新
    const nonReactiveLogs = [];
    
    const childLog = (event) => {
      const timestamp = new Date().toLocaleTimeString();
      nonReactiveLogs.push({
        event,
        timestamp
      });
      if (nonReactiveLogs.length > 10) {
        nonReactiveLogs.shift();
      }
      // 只在控制台打印，不在DOM中渲染
      console.log(`子组件 ${event} - ${timestamp}`);
    };
    
    onBeforeMount(() => childLog('onBeforeMount'));
    onMounted(() => childLog('onMounted'));
    // 移除更新钩子中的日志，避免递归
    // onBeforeUpdate(() => childLog('onBeforeUpdate'));
    // onUpdated(() => childLog('onUpdated'));
    onBeforeUnmount(() => childLog('onBeforeUnmount'));
    onUnmounted(() => childLog('onUnmounted'));
    
    return {};
  },
  template: `
    <div class="child-component">
      <h4>子组件生命周期</h4>
      <p>子组件生命周期日志已移至控制台输出</p>
      <p>避免DOM更新导致的递归问题</p>
    </div>
  `
};
</script>

<template>
  <div class="lifecycle-demo">
    <h2 class="demo-title">⏱️ Vue 3 生命周期钩子演示</h2>
    
    <div class="demo-grid">
      <!-- 生命周期控制 -->
      <div class="demo-card">
        <h3 class="card-title">生命周期控制</h3>
        <div class="demo-content">
          <div class="counter-section">
          <p>计数器: {{ counter }}</p>
          <button class="action-btn" @click="incrementCounter">触发更新</button>
          <button class="action-btn" @click="updateUserData">更新用户数据</button>
        </div>
        
        <div class="user-data-section" v-if="userData">
          <h4>用户数据 (业务数据示例)</h4>
          <p>用户名: {{ userData.name }}</p>
          <p>年龄: {{ userData.age }}</p>
          <p v-if="userData.lastUpdated">最后更新: {{ userData.lastUpdated }}</p>
        </div>
        
        <div class="server-message">
          <p :class="['status-message', { 'loading': isLoading }]">
            {{ serverMessage }}
          </p>
        </div>
          
          <div class="child-control">
            <p>子组件状态: {{ showChild ? '显示中' : '已卸载' }}</p>
            <button class="action-btn" @click="toggleChild">
              {{ showChild ? '卸载子组件' : '挂载子组件' }}
            </button>
          </div>
          
          <button class="action-btn secondary" @click="clearLogs">清除日志</button>
        </div>
      </div>

      <!-- 父组件日志 -->
      <div class="demo-card">
        <h3 class="card-title">父组件生命周期日志</h3>
        <div class="logs-container">
          <div 
            v-for="(log, index) in lifecycleLogs" 
            :key="index"
            class="log-entry"
            :style="{ color: log.color }"
          >
            {{ log.event }} - {{ log.timestamp }}
          </div>
          <div v-if="lifecycleLogs.length === 0" class="no-logs">
            暂无日志
          </div>
        </div>
      </div>
    </div>

    <!-- 子组件 -->
    <Transition name="fade">
      <LifecycleChild v-if="showChild" />
    </Transition>

    <!-- 生命周期说明 -->
    <div class="explanation">
      <h3>Vue 3 生命周期钩子说明</h3>
      <div class="lifecycle-timeline">
        <div class="timeline-item">
          <div class="timeline-dot" style="background: #4caf50;"></div>
          <div class="timeline-content">
            <strong>onBeforeMount</strong>
            <p>组件挂载到DOM前调用</p>
            <p class="business-scenario">业务场景: 准备初始数据、设置默认值、配置第三方库</p>
          </div>
        </div>
        <div class="timeline-item">
          <div class="timeline-dot" style="background: #2196f3;"></div>
          <div class="timeline-content">
            <strong>onMounted</strong>
            <p>组件挂载到DOM后调用，可访问DOM元素</p>
            <p class="business-scenario">业务场景: API请求、DOM操作、添加事件监听器、初始化图表</p>
          </div>
        </div>
        <div class="timeline-item">
          <div class="timeline-dot" style="background: #ff9800;"></div>
          <div class="timeline-content">
            <strong>onBeforeUpdate</strong>
            <p>组件更新前调用</p>
            <p class="business-scenario">业务场景: 数据验证、计算派生数据、准备更新所需资源</p>
          </div>
        </div>
        <div class="timeline-item">
          <div class="timeline-dot" style="background: #9c27b0;"></div>
          <div class="timeline-content">
            <strong>onUpdated</strong>
            <p>组件更新后调用，DOM已更新</p>
            <p class="business-scenario">业务场景: 滚动同步、触发动画、更新第三方库、保存表单状态</p>
          </div>
        </div>
        <div class="timeline-item">
          <div class="timeline-dot" style="background: #f44336;"></div>
          <div class="timeline-content">
            <strong>onBeforeUnmount</strong>
            <p>组件卸载前调用</p>
            <p class="business-scenario">业务场景: 清理定时器、移除事件监听器、取消API请求、保存用户会话</p>
          </div>
        </div>
        <div class="timeline-item">
          <div class="timeline-dot" style="background: #795548;"></div>
          <div class="timeline-content">
            <strong>onUnmounted</strong>
            <p>组件卸载后调用，可清理副作用</p>
            <p class="business-scenario">业务场景: 完全释放资源、取消所有订阅、清理全局状态</p>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
.lifecycle-demo {
  animation: fadeIn 0.5s ease-in-out;
}

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.demo-title {
  font-size: 2rem;
  margin-bottom: 2rem;
  color: #333;
  text-align: center;
}

.demo-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 2rem;
  margin-bottom: 2rem;
}

.demo-card {
  background: #f8f9fa;
  border-radius: 12px;
  padding: 1.5rem;
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
}

.card-title {
  font-size: 1.2rem;
  margin-bottom: 1rem;
  color: #667eea;
  font-weight: 600;
}

.demo-content {
  line-height: 1.8;
}

.counter-section,
.child-control,
.user-data-section,
.server-message {
  margin-bottom: 1rem;
  padding: 1rem;
  background: white;
  border-radius: 8px;
}

.user-data-section h4 {
  margin-top: 0;
  color: #2196f3;
}

.status-message {
  font-weight: 500;
  transition: color 0.3s;
}

.status-message.loading {
  color: #ff9800;
  animation: pulse 1.5s infinite;
}

@keyframes pulse {
  0% { opacity: 1; }
  50% { opacity: 0.6; }
  100% { opacity: 1; }
}

.action-btn {
  padding: 0.6rem 1.2rem;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  font-weight: 500;
  transition: all 0.3s ease;
  margin-top: 0.5rem;
}

.action-btn:not(.secondary) {
  background: #667eea;
  color: white;
}

.action-btn:not(.secondary):hover {
  background: #764ba2;
}

.action-btn.secondary {
  background: #6c757d;
  color: white;
  width: 100%;
}

.action-btn.secondary:hover {
  background: #5a6268;
}

.logs-container {
  max-height: 300px;
  overflow-y: auto;
  padding: 1rem;
  background: white;
  border-radius: 8px;
  font-family: monospace;
  font-size: 0.9rem;
}

.log-entry {
  padding: 0.3rem 0;
  border-bottom: 1px solid #eee;
}

.log-entry:last-child {
  border-bottom: none;
}

.no-logs {
  text-align: center;
  color: #999;
  padding: 2rem;
}

.child-component {
  background: #e3f2fd;
  border-radius: 12px;
  padding: 1.5rem;
  margin-bottom: 2rem;
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
}

.child-component h4 {
  color: #1565c0;
  margin-bottom: 1rem;
}

.child-logs {
  max-height: 200px;
}

.explanation {
  background: #f3e5f5;
  border-radius: 12px;
  padding: 2rem;
  margin-top: 2rem;
}

.explanation h3 {
  color: #7b1fa2;
  margin-bottom: 1.5rem;
  text-align: center;
}

.lifecycle-timeline {
  position: relative;
  max-width: 800px;
  margin: 0 auto;
}

.lifecycle-timeline::before {
  content: '';
  position: absolute;
  width: 4px;
  background-color: #ddd;
  top: 0;
  bottom: 0;
  left: 20px;
  margin-left: -2px;
}

.timeline-item {
  padding: 10px 40px;
  position: relative;
  background-color: inherit;
  margin-bottom: 1rem;
}

.timeline-dot {
  position: absolute;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background-color: white;
  border: 4px solid #667eea;
  left: 12px;
  top: 15px;
  z-index: 1;
}

.timeline-content {
  padding: 1rem;
  background-color: white;
  border-radius: 8px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}

.timeline-content strong {
  display: block;
  margin-bottom: 0.5rem;
}

.timeline-content p {
  margin: 0.25rem 0;
  color: #666;
  font-size: 0.9rem;
}

.business-scenario {
  font-style: italic;
  color: #7b1fa2 !important;
  margin-top: 0.5rem;
  font-size: 0.85rem;
}

/* 过渡动画 */
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s ease;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}

@media (max-width: 768px) {
  .demo-grid {
    grid-template-columns: 1fr;
  }
  
  .demo-title {
    font-size: 1.6rem;
  }
  
  .lifecycle-timeline::before {
    left: 15px;
  }
  
  .timeline-dot {
    left: 7px;
  }
}
</style>